package ar.edu.utn.frsf.proyectofinal.sistemadecontrolmodbus.devicecontroller.factories.imp;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.LinkedBlockingQueue;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import net.wimpi.modbus.Modbus;
import net.wimpi.modbus.msg.ModbusRequest;
import net.wimpi.modbus.msg.ReadCoilsRequest;
import net.wimpi.modbus.msg.ReadInputDiscretesRequest;
import net.wimpi.modbus.msg.ReadInputRegistersRequest;
import net.wimpi.modbus.msg.ReadMultipleRegistersRequest;
import ar.edu.utn.frsf.proyectofinal.sistemadecontrolmodbus.persistence.DeviceDAO;
import ar.edu.utn.frsf.proyectofinal.sistemadecontrolmodbus.persistence.model.AddressValue;
import ar.edu.utn.frsf.proyectofinal.sistemadecontrolmodbus.persistence.model.Device;
import ar.edu.utn.frsf.proyectofinal.sistemadecontrolmodbus.persistence.model.SoundRequest;
import ar.edu.utn.frsf.proyectofinal.sistemadecontrolmodbus.devicecontroller.DeviceController;
import ar.edu.utn.frsf.proyectofinal.sistemadecontrolmodbus.devicecontroller.command.Command;
import ar.edu.utn.frsf.proyectofinal.sistemadecontrolmodbus.devicecontroller.factories.DeviceControllerFactory;
import ar.edu.utn.frsf.proyectofinal.sistemadecontrolmodbus.devicecontroller.imp.DeviceControllerImp;
import ar.edu.utn.frsf.proyectofinal.sistemadecontrolmodbus.devicecontroller.imp.ProcessImageImp;
import ar.edu.utn.frsf.proyectofinal.sistemadecontrolmodbus.devicecontroller.messageprocessors.RequestProcessor;
import ar.edu.utn.frsf.proyectofinal.sistemadecontrolmodbus.devicecontroller.messageprocessors.ResponseProcessor;
import ar.edu.utn.frsf.proyectofinal.sistemadecontrolmodbus.devicecontroller.messageprocessors.RequestProcessorImp.AbstractRequestProcessor;
import ar.edu.utn.frsf.proyectofinal.sistemadecontrolmodbus.devicecontroller.messageprocessors.RequestProcessorImp.ErrorRequestProcessor;
import ar.edu.utn.frsf.proyectofinal.sistemadecontrolmodbus.devicecontroller.messageprocessors.RequestProcessorImp.WriteCoilRequestProcessor;
import ar.edu.utn.frsf.proyectofinal.sistemadecontrolmodbus.devicecontroller.messageprocessors.RequestProcessorImp.WriteMultipleCoilsRequestProcessor;
import ar.edu.utn.frsf.proyectofinal.sistemadecontrolmodbus.devicecontroller.messageprocessors.RequestProcessorImp.WriteMultipleRegistersRequestProcessor;
import ar.edu.utn.frsf.proyectofinal.sistemadecontrolmodbus.devicecontroller.messageprocessors.RequestProcessorImp.WriteSingleRegisterRequestProcessor;
import ar.edu.utn.frsf.proyectofinal.sistemadecontrolmodbus.devicecontroller.messageprocessors.ResponseProcessorImp.AbstractResponseProcessor;
import ar.edu.utn.frsf.proyectofinal.sistemadecontrolmodbus.devicecontroller.messageprocessors.ResponseProcessorImp.ReadCoilsResponseProcessor;
import ar.edu.utn.frsf.proyectofinal.sistemadecontrolmodbus.devicecontroller.messageprocessors.ResponseProcessorImp.ReadInputDiscretesResponseProcessor;
import ar.edu.utn.frsf.proyectofinal.sistemadecontrolmodbus.devicecontroller.messageprocessors.ResponseProcessorImp.ReadInputRegisterResponseProcessor;
import ar.edu.utn.frsf.proyectofinal.sistemadecontrolmodbus.devicecontroller.messageprocessors.ResponseProcessorImp.ReadMultipleResgistersResponseProcessor;
import ar.edu.utn.frsf.proyectofinal.sistemadecontrolmodbus.devicecontroller.messageprocessors.ResponseProcessorImp.VoidErrorResponseProcessor;
import ar.edu.utn.frsf.proyectofinal.sistemadecontrolmodbus.devicecontroller.messageprocessors.ResponseProcessorImp.WriteCoilResponseProcessor;
import ar.edu.utn.frsf.proyectofinal.sistemadecontrolmodbus.devicecontroller.messageprocessors.ResponseProcessorImp.WriteSingleRegisterResponseProcessor;

@Component
public class DeviceControllerFactoryImp implements DeviceControllerFactory {


	@Autowired(required=true)
	private DeviceDAO deviceDAO;
	
	@Override
	public DeviceController create(Device device) {
		
		DeviceControllerImp dev = new DeviceControllerImp();
		ProcessImageImp pImage = new ProcessImageImp();
		dev.setImage(pImage);

		Map<Integer,RequestProcessor>  requestProcessor;
		RequestProcessor requestErrorProcessor;
		Map<Integer,ResponseProcessor> responseProcessor;
		ResponseProcessor responseErrorProcessor;
		
		responseErrorProcessor = new VoidErrorResponseProcessor();
		responseProcessor      = new HashMap<Integer, ResponseProcessor>();
		responseProcessor.put(Modbus.WRITE_COIL, new WriteCoilResponseProcessor());
		responseProcessor.put(Modbus.WRITE_SINGLE_REGISTER, new WriteSingleRegisterResponseProcessor());
		responseProcessor.put(Modbus.READ_COILS, new ReadCoilsResponseProcessor());
		responseProcessor.put(Modbus.READ_INPUT_DISCRETES, new ReadInputDiscretesResponseProcessor());
		responseProcessor.put(Modbus.READ_INPUT_REGISTERS, new ReadInputRegisterResponseProcessor());
		responseProcessor.put(Modbus.READ_MULTIPLE_REGISTERS, new ReadMultipleResgistersResponseProcessor());
		
		requestErrorProcessor  = new ErrorRequestProcessor();
		requestProcessor       = new HashMap<Integer, RequestProcessor>();
		requestProcessor.put(Modbus.WRITE_COIL, new WriteCoilRequestProcessor());
		requestProcessor.put(Modbus.WRITE_MULTIPLE_COILS, new WriteMultipleCoilsRequestProcessor());
		requestProcessor.put(Modbus.WRITE_MULTIPLE_REGISTERS, new WriteMultipleRegistersRequestProcessor());
		requestProcessor.put(Modbus.WRITE_SINGLE_REGISTER, new WriteSingleRegisterRequestProcessor());
		
		@SuppressWarnings("rawtypes")
		Iterator it = responseProcessor.values().iterator();
		while(it.hasNext() ){
			AbstractResponseProcessor c = (AbstractResponseProcessor) it.next();
			c.setImage(pImage);
			c.setDeciveDAO(deviceDAO);
			c.setDevice(device);
		}
		
		((AbstractResponseProcessor) responseErrorProcessor).setImage(pImage);
		
		it = requestProcessor.values().iterator();
		while(it.hasNext() ){
			AbstractRequestProcessor c = (AbstractRequestProcessor) it.next();
			c.setImage(pImage);
			c.setDeciveDAO(deviceDAO);
			c.setDevice(device);
		}

		((AbstractRequestProcessor) requestErrorProcessor).setImage(pImage);
		
		dev.setRequestErrorProcessor(requestErrorProcessor);
		dev.setRequestProcessor(requestProcessor);
		dev.setResponseErrorProcessor(responseErrorProcessor);
		dev.setResponseProcessor(responseProcessor);
		dev.setCommandsQueue( new LinkedBlockingQueue<Command>());
		dev.setNewValues(new LinkedBlockingQueue<AddressValue>());
		
		dev.setTransactionNumber(0);
		dev.setPoolingTime(0);
		if(device != null){
			dev.setUnitNumber(device.getUnitID().intValue());
			dev.setDevice(device);
			dev.setDeviceDAO(deviceDAO);
		}
		if(device != null && device.getSounding()){
			dev.setPoolingRequests(soundindRequests(device));
			dev.setPoolingTime(device.getSoundingTime());
		}
		
		return dev;
	}

	public Vector<ModbusRequest> soundindRequests(Device device) {
		Vector<ModbusRequest> soundindRequests = new Vector<ModbusRequest>();
		for (SoundRequest modbusRequest : deviceDAO.getSoundRequests(device)) {
			soundindRequests.add(decodeRequest(modbusRequest));
		}
		return soundindRequests;
	}

	private ModbusRequest decodeRequest(SoundRequest modbusRequest) {
		ModbusRequest ret =null;
		switch(modbusRequest.getAddressType()){
			case COIL:
				ret = new ReadCoilsRequest(modbusRequest.getAddress(), modbusRequest.getCount());
				break;
			case DISCRETE:
				ret = new ReadInputDiscretesRequest(modbusRequest.getAddress(), modbusRequest.getCount());
				break;
			case HOLDING_REGISTER:
				ret = new ReadMultipleRegistersRequest(modbusRequest.getAddress(), modbusRequest.getCount());
				break;
			case INPUT_REGISTER:
				ret = new ReadInputRegistersRequest(modbusRequest.getAddress(), modbusRequest.getCount());
				break;
			default:
				break;
		}
		return ret;
	}

	/**
	 * @return the deviceDAO
	 */
	public DeviceDAO getDeviceDAO() {
		return deviceDAO;
	}

	/**
	 * @param deviceDAO the deviceDAO to set
	 */
	public void setDeviceDAO(DeviceDAO deviceDAO) {
		this.deviceDAO = deviceDAO;
	}
}
